import fetch from 'node-fetch'
import qs from 'node:querystring'

export class Captcha extends plugin {
  constructor () {
    super({
      name: 'QQ登录验证码',
      dsc: '登录验证码',
      route: '/captcha',
      rule: [
        {
          method: 'get',
          path: '/slider',
          fnc: 'captchaRender'
        },
        {
          method: 'post',
          path: '/slider',
          fnc: 'captchaSubmit'
        },
        {
          method: 'ws',
          path: '/slider',
          fnc: 'connect'
        },
        {
          method: ['get', 'post'],
          path: '/:key/localhost:8302/:path',
          fnc: 'repostRequest',
          use: 'useCors'
        }
      ]
    })
  }

  init () {
    Object.assign(this.constructor, {
      users: { socket: {} },
      uuid () {
        function S4 () {
          return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1)
        }
        return (S4() + S4() + '-' + S4() + '-' + S4() + '-' + S4() + '-' + S4() + S4() + S4())
      },
      socketWrite (socket, type, payload, echo) {
        try {
          socket.send(JSON.stringify({ type, payload, echo }))
        } catch {}
      },
      socketSend (key, type, payload) {
        let socket = this.users.socket[key]
        let echo = this.uuid()
        if (!socket) return false
        return new Promise((resolve, reject) => {
          this.wait[echo] = resolve
          this.socketWrite(socket, type, payload, echo)
          setTimeout(() => resolve(0) && delete this.wait[echo], 5 * 1000)
        })
      }
    })
  }

  connect () {
    let key = this.request?.query?.key
    if (!key) return this.close()
    Captcha.users.socket[key] = this
    Captcha.wait ||= {}

    this.params = { key }
    this.json = () => {}

    this.onMessage((data) => {
      try {
        data = JSON.parse(data)
        let { echo, type, payload } = data
        Captcha.wait[echo]?.call({}, payload) || (this[type] && this[type]({ key, ...payload }, echo))
      } catch (err) {
        logger.error(err)
      }
    })
  }

  captchaRender () {
    let user = Captcha.users[this.params.key]
    if (user && !user.body) {
      this.render('captcha', user)
    } else {
      this.send('<title>验证码</title>没有验证码')
    }
  }

  async captchaSubmit () {
    let handler = {
      register: ['key', 'url'],
      saveTicket: ['key', 'ticket'],
      getTicket: ['submit']
    }

    for (let i in handler) {
      let check = handler[i].map(val => '' + !!this.params[val])
      let fn = !check.includes('false') && i
      if (fn) return await this[fn](this.params)
    }

    this.json()
  }

  async register ({ key, url }) {
    let script = ''
    if (!url.includes('TCaptcha.js')) {
      url = `https://ssl.captcha.qq.com/template/TCapIframeApi.js${url.replace(/[^?]+\?/, '?')}`
      if (this.params.v2 === '' || this.params.v2) {
        let text = await (await fetch(url)).text()
        script = text.replace(/https:\/\/t\.captcha\.qq\.com/g, `${Server.cfg.http.PUBLIC_ADDRESS}/${key}/localhost:8302`)
      }
    }
    Captcha.users[key] = { url, script }
    setTimeout(() => {
      delete Captcha.users[key]
      delete Captcha.users.socket[key]
    }, 120 * 1000)
    this.json()
  }

  saveTicket ({ key }) {
    if (Captcha.users[key]) {
      Captcha.users[key].body = this.body
    }
    if (Captcha.socketSend(key, 'ticket', this.body)) {
      setTimeout(() => {
        delete Captcha.users[key]
        delete Captcha.users.socket[key]
      }, 800)
      logger.info(`CaptchaSubmit: ${key}`)
    }
    this.json()
  }

  getTicket ({ key }) {
    key = this.params.submit || key
    let user = Captcha.users[key]
    if (!user) return this.json()

    let data = null
    let { body } = user
    if (body) {
      data = body
      delete Captcha.users[key]
      logger.info(`CaptchaSubmit: ${key}`)
    }
    this.json(data)
  }

  json (data = null) {
    this.send({ status: 0, message: 'OK', data })
  }

  async repostRequest () {
    let { key, path } = this.params

    let user = Captcha.users[key]
    if (!user) return this.res.status(500).end()

    let { method, headers } = this.req
    headers.host = 't.captcha.qq.com'
    if (headers.referer?.includes(Server.cfg.http.PUBLIC_ADDRESS)) {
      headers.referer = 'https://ti.qq.com/'
    }
    delete headers.cookie
    let data = {
      url: `https://t.captcha.qq.com/${path}`,
      method,
      headers
    }
    logger.info(data)
    if (data.method == 'GET') {
      if (this.query?.entry_url) this.query.entry_url = 'https://ti.qq.com/safe/tools/captcha/sms-verify-login'
      data.url += `?${qs.stringify(this.query)}`
    } else {
      data.body = qs.stringify(this.body)
    }

    let payload = await Captcha.socketSend(key, 'handle', data)
    if (payload) {
      let { result } = payload
      let buf = Buffer.from(result, 'base64')
      return this.res.send(buf)
    }

    this.res.status(500).end()
  }
}
